// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_entity_sky_box.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_camera.h"
#include "mk_render_define.h"
#include "mk_render_device.h"
#include "mk_texture.h"
#include "mk_shader.h"
#include "mk_render_input.h"
#include "mk_scene_node.h"
#include "mk_resource_manager.h"

namespace MK3DE
{
	CEntitySkyBox::CEntitySkyBox(CScene* pScene)
		: CEntity(pScene)
	{
		_renderInput = NULL;
		_vertexBuffer = NULL;
		_shader = NULL;
		memset(_texture, 0, sizeof(CTexture2D*) * SBP_MAX);
	}

	CEntitySkyBox::~CEntitySkyBox()
	{
		CRenderDevice* rd = GetEngine()->GetRenderDevice(); 
		SAFE_RELEASE(_vertexBuffer);
		SAFE_RELEASE(_shader);
		rd->DestroyRenderInput(_renderInput);
		for (int i = 0; i < SBP_MAX; ++i)
			SAFE_RELEASE(_texture[i]);
	}

	BOOL CEntitySkyBox::SetTexture(SKY_BOX_PLANE plane, const char* fileName)
	{
		BOOL ret = GetEngine()->GetResourceManager()->GetTexture2DFromFile(fileName, _texture[plane]);
		MK_PROCESS_ERROR(ret);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CEntitySkyBox::Create(const char* shaderFile, float size)
	{
		BOOL ret = FALSE;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		CResourceManager* rm = GetEngine()->GetResourceManager();

		ret = rm->GetShaderFromFile(shaderFile, _shader);
		MK_PROCESS_ERROR(ret);

		ret = rm->GetVertexBuffer(sizeof(VERT_3D_TEX1), 24, HBU_STATIC, _vertexBuffer);
		MK_PROCESS_ERROR(ret);

		RenderInputElemDesc rie[] = { 
			RenderInputElemDesc(SU_POSITION, 0, 0, 0),
			RenderInputElemDesc(SU_TEXCOORD, 0, 12, 0)
		};

		ret = rd->CreateRenderInput(rie, 2, _renderInput);
		MK_PROCESS_ERROR(ret);

		ret = _renderInput->SetVertexBuffer(0, _vertexBuffer);
		MK_PROCESS_ERROR(ret);

		VERT_3D_TEX1* pData;
		ret = _vertexBuffer->Lock((void*&)pData, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		size *= 0.5f;
		// left
		pData->pos = D3DXVECTOR3(-size, size, -size);  pData->uv = D3DXVECTOR2(0, 0); pData++;
		pData->pos = D3DXVECTOR3(-size, size, size);   pData->uv = D3DXVECTOR2(1, 0); pData++;
		pData->pos = D3DXVECTOR3(-size, -size, -size); pData->uv = D3DXVECTOR2(0, 1); pData++;
		pData->pos = D3DXVECTOR3(-size, -size, size);  pData->uv = D3DXVECTOR2(1, 1); pData++;

		// right
		pData->pos = D3DXVECTOR3(size, size, size);    pData->uv = D3DXVECTOR2(0, 0); pData++;
		pData->pos = D3DXVECTOR3(size, size, -size);   pData->uv = D3DXVECTOR2(1, 0); pData++;
		pData->pos = D3DXVECTOR3(size, -size, size);   pData->uv = D3DXVECTOR2(0, 1); pData++;
		pData->pos = D3DXVECTOR3(size, -size, -size);  pData->uv = D3DXVECTOR2(1, 1); pData++;

		// top
		pData->pos = D3DXVECTOR3(size, size, -size);    pData->uv = D3DXVECTOR2(0, 0); pData++;
		pData->pos = D3DXVECTOR3(size, size, size);     pData->uv = D3DXVECTOR2(1, 0); pData++;
		pData->pos = D3DXVECTOR3(-size, size, -size);   pData->uv = D3DXVECTOR2(0, 1); pData++;
		pData->pos = D3DXVECTOR3(-size, size, size);    pData->uv = D3DXVECTOR2(1, 1); pData++;

		// bottom
		pData->pos = D3DXVECTOR3(-size, -size, -size);  pData->uv = D3DXVECTOR2(0, 0); pData++;
		pData->pos = D3DXVECTOR3(-size, -size, size);   pData->uv = D3DXVECTOR2(1, 0); pData++;
		pData->pos = D3DXVECTOR3(size, -size, -size);   pData->uv = D3DXVECTOR2(0, 1); pData++;
		pData->pos = D3DXVECTOR3(size, -size, size);    pData->uv = D3DXVECTOR2(1, 1); pData++;

		// front
		pData->pos = D3DXVECTOR3(-size, size, size);    pData->uv = D3DXVECTOR2(0, 0); pData++;
		pData->pos = D3DXVECTOR3(size, size, size);     pData->uv = D3DXVECTOR2(1, 0); pData++;
		pData->pos = D3DXVECTOR3(-size, -size, size);   pData->uv = D3DXVECTOR2(0, 1); pData++;
		pData->pos = D3DXVECTOR3(size, -size, size);    pData->uv = D3DXVECTOR2(1, 1); pData++;

		// back
		pData->pos = D3DXVECTOR3(size, size, -size);    pData->uv = D3DXVECTOR2(0, 0); pData++;
		pData->pos = D3DXVECTOR3(-size, size, -size);   pData->uv = D3DXVECTOR2(1, 0); pData++;
		pData->pos = D3DXVECTOR3(size, -size, -size);   pData->uv = D3DXVECTOR2(0, 1); pData++;
		pData->pos = D3DXVECTOR3(-size, -size, -size);  pData->uv = D3DXVECTOR2(1, 1); pData++;

		ret = _vertexBuffer->Unlock();
		MK_PROCESS_ERROR(ret);

		ret = TRUE;

Exit0:
		return ret;
	}

	void CEntitySkyBox::Render()
	{
		if (!_sceneNode || !_bVisible)
			return;

		BOOL ret = FALSE;
		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		rd->BindRenderInput(_renderInput);

		CEngine* engine = GetEngine();
		CCamera* camera = engine->GetCurrentCamera();
		D3DXVECTOR3 cameraPos = camera->GetPosition();
		D3DXMATRIX& absTransform = _sceneNode->GetAbsoluteTransformation();
		absTransform._41 = cameraPos.x;
		absTransform._42 = cameraPos.y;
		absTransform._43 = cameraPos.z;
		D3DXMATRIX mvp = absTransform * camera->GetViewProjMatrix();
		_shader->SetMatrix(SCN_MAT_WORLD_VIEW_PROJ, &mvp);

		DWORD passNum;
		ret = _shader->TechniqueBegin(TECH_TERRAIN_SKY_BOX, passNum);
		MK_PROCESS_ERROR(ret);

		for (DWORD pass = 0; pass < passNum; ++pass)
		{
			for (int i = 0; i < (int)6; ++i)
			{
				ret = _shader->SetTexture(SCN_SKY_BOX_TEXTURE, _texture[i]);
				MK_PROCESS_ERROR(ret);

				if (_shader->PassBegin(pass))
				{
					ret = rd->DrawPrimitive(PT_TRIANGLESTRIP, i * 4, 0, 24, 0, 2);
					MK_PROCESS_ERROR(ret);
					_shader->PassEnd();
				}
			}
		}

		_shader->TechniqueEnd();

Exit0:
		return;
	}
}